﻿using System;
using System.Configuration.Install;
using System.ComponentModel;
using System.Xml;

using Microsoft.Win32;

namespace AzureStorageProvider.Setup.CustomAction
{
    [RunInstaller(true)]
    public sealed class MachineConfigHelper : Installer
    {
        public override void  Install(System.Collections.IDictionary stateSaver)
        {
            Register(MachineConfigFileV2);
            Register(MachineConfigFileV4Client);
            Register(MachineConfigFileV4Full);

            base.Install(stateSaver);
        }

        public override void  Uninstall(System.Collections.IDictionary savedState)
        {
            Unregister(MachineConfigFileV2);
            Unregister(MachineConfigFileV4Client);
            Unregister(MachineConfigFileV4Full);

            base.Uninstall(savedState);
        }

        #region Implementation

        private static void Register(string path)
        {
            bool dirty = false;

            // Quick'n Dirty
            // TODO: rollback changes

            XmlDocument config = new XmlDocument();
            config.Load(path);

            // <configuration>
            //   <system.serviceModel>
            //     <extensions>
            //       <bindingElementExtensions>
            //         <add name="azureStorageAdapter" type="BizTalkFactory.Adapters.AzureStorage.AzureStorageAdapterBindingElementExtensionElement, BizTalkFactory.Adapters.AzureStorage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6ca9668807bd3a5f" />

            XmlNode bindingElementExtensions = config.SelectSingleNode(@"configuration/system.serviceModel/extensions/bindingElementExtensions/add[@name='azureStorageAdapter']");
            if (bindingElementExtensions == null)
            {
                bindingElementExtensions = config.SelectSingleNode(@"configuration/system.serviceModel/extensions/bindingElementExtensions");
                if (bindingElementExtensions == null) throw new ArgumentException("no <configuration><system.serviceModel><extensions><bindingElementExtensions> section");

                XmlElement adapter = config.CreateElement("add");
                adapter.SetAttribute("name", "azureStorageAdapter");
                adapter.SetAttribute("type", "BizTalkFactory.Adapters.AzureStorage.AzureStorageAdapterBindingElementExtensionElement, BizTalkFactory.Adapters.AzureStorage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6ca9668807bd3a5f");
                bindingElementExtensions.AppendChild(adapter);

                dirty = true;
            }

            // <configuration>
            //   <system.serviceModel>   
            //     <extensions>
            //       <bindingExtensions>
            //         <add name="azureStorageBinding" type="BizTalkFactory.Adapters.AzureStorage.AzureStorageAdapterBindingCollectionElement, BizTalkFactory.Adapters.AzureStorage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6ca9668807bd3a5f" />

            XmlNode bindingExtensions = config.SelectSingleNode(@"configuration/system.serviceModel/extensions/bindingExtensions/add[@name='azureStorageBinding']");
            if (bindingExtensions == null)
            {
                bindingExtensions = config.SelectSingleNode(@"configuration/system.serviceModel/extensions/bindingExtensions");
                if (bindingExtensions == null) throw new ArgumentException("no <configuration><system.serviceModel><extensions><bindingExtensions> section");

                XmlElement add = config.CreateElement("add");
                add.SetAttribute("name", "azureStorageBinding");
                add.SetAttribute("type", "BizTalkFactory.Adapters.AzureStorage.AzureStorageAdapterBindingCollectionElement, BizTalkFactory.Adapters.AzureStorage, Version=1.0.0.0, Culture=neutral, PublicKeyToken=6ca9668807bd3a5f");
                bindingExtensions.AppendChild(add);

                dirty = true;
            }

            //  <configuration>
            //    <system.serviceModel>
            //      <extensions>
            //        <client>
            //          <endpoint binding="azureStorageBinding" contract="IMetadataExchange" name="azure" />

            XmlNode client = config.SelectSingleNode(@"configuration/system.serviceModel/client/endpoint[@binding='azureStorageBinding']");
            if (client == null)
            {
                client = config.SelectSingleNode(@"configuration/system.serviceModel/client");
                if (client == null) throw new ArgumentException("no <configuration><system.serviceModel><client> section");

                XmlElement endpoint = config.CreateElement("endpoint");
                endpoint.SetAttribute("binding", "azureStorageBinding");
                endpoint.SetAttribute("contract", "IMetadataExchange");
                endpoint.SetAttribute("name", "azure");
                client.AppendChild(endpoint);

                dirty = true;
            }

            // save

            if (dirty) config.Save(path);
        }

        private static void Unregister(string path)
        {
            bool dirty = false;

            // Quick'n Dirty
            // TODO: handle errors

            XmlDocument config = new XmlDocument();
            config.Load(path);

            // bindingElementExtensions/add[name='azureStorageAdapter']
            XmlNode bindingElementExtensions = config.SelectSingleNode(@"configuration/system.serviceModel/extensions/bindingElementExtensions/add[@name='azureStorageAdapter']");
            if (bindingElementExtensions != null)
            {
                bindingElementExtensions.ParentNode.RemoveChild(bindingElementExtensions);
                dirty = true;
            }

            // bindingExtensions/add[@name='azureStorageBinding']
            XmlNode bindingExtensions = config.SelectSingleNode(@"configuration/system.serviceModel/extensions/bindingExtensions/add[@name='azureStorageBinding']");
            if (bindingExtensions != null)
            {
                bindingExtensions.ParentNode.RemoveChild(bindingExtensions);
                dirty = true;
            }

            // client/binding='azureStorageBinding']
            XmlNode client = config.SelectSingleNode(@"configuration/system.serviceModel/client/endpoint[@binding='azureStorageBinding']");
            if (client != null)
            {
                client.ParentNode.RemoveChild(client);
                dirty = true;
            }

            // save

            if (dirty) config.Save(path);
        }

        #endregion Implementation

        /// <summary>
        /// Obtain the path of the .Net Framework machine.config
        /// 
        /// http://stackoverflow.com/questions/199080/how-to-detect-what-net-framework-versions-and-service-packs-are-installed
        /// 
        /// Framework Version  Registry Key
        /// ------------------------------------------------------------------------------------------
        /// 1.0                HKLM\Software\Microsoft\.NETFramework\Policy\v1.0\3705 
        /// 1.1                HKLM\Software\Microsoft\NET Framework Setup\NDP\v1.1.4322\Install 
        /// 2.0                HKLM\Software\Microsoft\NET Framework Setup\NDP\v2.0.50727\Install 
        /// 3.0                HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.0\Setup\InstallSuccess 
        /// 3.5                HKLM\Software\Microsoft\NET Framework Setup\NDP\v3.5\Install 
        /// 4.0 Client Profile HKLM\Software\Microsoft\NET Framework Setup\NDP\v4\Client\Install
        /// 4.0 Full Profile   HKLM\Software\Microsoft\NET Framework Setup\NDP\v4\Full\Install   
        /// 
        /// </summary>
        /// <param name="version"></param>
        /// <returns></returns>

        private const string NETFX_REGISTRY_V2 = @"Software\Microsoft\NET Framework Setup\NDP\v2.0.50727";
        private const string NETFX_REGISTRY_V4_CLIENT = @"Software\Microsoft\NET Framework Setup\NDP\v4\Client";
        private const string NETFX_REGISTRY_V4_FULL = @"Software\Microsoft\NET Framework Setup\NDP\v4\Full";

        private static string GetMachineConfigFilePath(string registry)
        {
            // check Net Framework installed

            RegistryKey hKey = Registry.LocalMachine.OpenSubKey(registry);
            if ((hKey == null) || ((System.Int32)hKey.GetValue("Install") != 1))
                return String.Empty;

            // retrieve Net Framework path

            string path = String.Empty;
            if (registry == NETFX_REGISTRY_V2)
                path = Environment.ExpandEnvironmentVariables(@"%windir%\Microsoft.Net\Framework\v2.0.50727");

            else
                path = (string)hKey.GetValue("InstallPath");

            if (!String.IsNullOrEmpty(path))
                return System.IO.Path.Combine(path, @"CONFIG\machine.config");

            return String.Empty;
        }

        /// <summary>
        /// Obtain the path of the .Net Framework v2.0.50727 machine.config
        /// </summary>
        private static string MachineConfigFileV2 { get { return GetMachineConfigFilePath(NETFX_REGISTRY_V2); } }

        /// <summary>
        /// Obtain the path of the .Net Framework v4.0 (Client Profile) machine.config
        /// </summary>
        private static string MachineConfigFileV4Client { get { return GetMachineConfigFilePath(NETFX_REGISTRY_V4_CLIENT); } }

        /// <summary>
        /// Obtain the path of the .Net Framework v4.0 (Full Profile) machine.config
        /// </summary>
        private static string MachineConfigFileV4Full { get { return GetMachineConfigFilePath(NETFX_REGISTRY_V4_FULL); } }
    }
}
